<script setup lang="ts">
import {
  DataTableColumns,
  FormInst,
  FormItemRule,
  FormRules,
  NButton,
  NButtonGroup,
  NPopconfirm,
  NSpace,
  NTag,
  TreeSelectOption,
} from "naive-ui";
import {SysMenu} from "@/api/menu/types.ts";
import {menuApi} from "@/api";
import {useDictStore} from "@/stores";
import {renderIcon, renderPermission, resetToNull} from "@/utils";
import {resolveDirective, vShow, withDirectives} from "vue";

enum ModeTypeEnum {
  ADD = '新增',
  MODIFY = '修改',
  DETAIL = '详情'
}

enum MenuTypeEnum {
  DIRECTORY = 0,
  MENU = 1,
  BUTTON = 2,
  LINK = 3,
  ENDPOINT = 4,
}

const dictStore = useDictStore();
const tableLoading = ref(false);
const tableColumns = ref<DataTableColumns<SysMenu>>([
  {title: '菜单名称', key: 'menuName', resizable: true},
  {title: '菜单编码', key: 'permissionCode', resizable: true},
  {
    title: '菜单图标',
    key: 'menuIcon',
    render: row => h(renderIcon(row.menuIcon, {class: 'align-text-bottom', size: 20})),
    resizable: true
  },
  {title: '菜单类型', key: 'menuType', render: row => dictStore.renderTag('menu-type', row.menuType), resizable: true},
  {title: '菜单路径', key: 'menuPath', resizable: true},
  {
    title: '菜单状态',
    key: 'menuStatus',
    render: row => dictStore.renderTag('common-status', row.menuStatus),
    resizable: true
  },
  {title: '菜单权重', key: 'menuWeight', resizable: true},
  {
    title: '操作', key: 'action', align: 'center', render: row => tableActionRender(row)
  }
])
const tableData = ref<SysMenu[]>([])

const drawerDetailShow = ref(false)
const drawerDetailTitle = ref<ModeTypeEnum | null>(null)
const drawerDetailFormRef = ref<FormInst | null>(null)
const drawerDetailFormParentTreeOptions = ref<SysMenu[]>([])
const drawerDetailFormRules = reactive<FormRules>({
  menuName: {required: true, message: '请输入菜单名称', trigger: 'blur'},
  permissionCode: {required: true, message: '请输入菜单编码', trigger: 'blur'},
  menuPath: {
    required: true, trigger: 'blur', validator: (rule: FormItemRule, value: string) => {
      return drawerDetailFormModel.menuType !== MenuTypeEnum.BUTTON && !value ? new Error('请输入菜单路径') : true;
    }
  },
  componentPath: {
    required: true, trigger: 'blur', validator: (rule: FormItemRule, value: string) => {
      return drawerDetailFormModel.menuType === MenuTypeEnum.MENU && !value ? new Error('请输入组件路径') : true;
    }
  },
  httpMethod: {
    required: true, trigger: 'blur', validator: (rule: FormItemRule, value: string) => {
      return drawerDetailFormModel.menuType === MenuTypeEnum.ENDPOINT && !value ? new Error('请选择请求方式') : true;
    }
  },
})
const drawerDetailFormModel = reactive<SysMenu>(<SysMenu>{})
const drawerDetailFormLoading = ref(false)
const modalWeightsShow = ref(false)
const modalWeightsSubmitLoading = ref(false)
const modalParentId = ref<number | null>(null)
const modalWeightsModel = ref<SysMenu[]>([])


onMounted(() => {
  dictStore.loadDict(['menu-type', 'common-status', 'http-method'])
  handleClickQuery()
})
const tableActionRender = (row: SysMenu) => {
  let hasChildren = row.children && row.children.length > 0;
  return h(NButtonGroup, {size: 'small'}, {
    default: () => {
      return [
        withDirectives(h(NButton, {
          type: 'info',
          tertiary: true,
          onClick: () => handleClickAddChildren(row)
        }, {default: () => '新增子项'}), [
          [vShow, MenuTypeEnum.DIRECTORY === row.menuType || MenuTypeEnum.MENU === row.menuType],
          [resolveDirective('p'), 'menu:add']
        ]),
        renderPermission(h(NButton, {
          tertiary: true,
          onClick: () => handleClickDetail(row)
        }, {default: () => '详情'}), 'menu:detail'),
        renderPermission(h(NButton, {
          type: 'primary',
          tertiary: true,
          onClick: () => handleClickModify(row)
        }, {default: () => '编辑'}), 'menu:modify'),
        h(NPopconfirm, {onPositiveClick: () => handleClickDelete(row)}, {
          trigger: () => renderPermission(h(NButton, {
            tertiary: true,
            type: 'error',
          }, {default: () => '删除'}), 'menu:delete'),
          default: () => `确定删除该(${row.menuName}/${row.permissionCode})字典吗？`
        }),
        hasChildren && renderPermission(h(NButton, {
          tertiary: true,
          onClick: () => handleClickWeights(row),
        }, {default: () => '排序'}), 'menu:weights'),
      ]
    }
  })
}
const handleClickQuery = () => {
  tableLoading.value = true
  menuApi.tree({}).then(res => {
    tableData.value = res.data
  }).finally(() => tableLoading.value = false)
}
const drawerDetailFormTreeLabel = (info: { option: TreeSelectOption, checked: boolean, selected: boolean }) => {
  return h(NSpace, {}, {
    default: () => {
      return [
        renderIcon(info.option.menuIcon).call(),
        h('span', {}, {default: () => info.option.menuName})
      ]
    }
  })
}
const initDrawerDetail = (type: ModeTypeEnum) => {
  drawerDetailTitle.value = type
  drawerDetailShow.value = true
  menuApi.tree({directoryAndMenu: true}).then(res => {
    drawerDetailFormParentTreeOptions.value = res.data
  })
}
const handleAddMenu = () => {
  initDrawerDetail(ModeTypeEnum.ADD)
  drawerDetailFormModel.menuType = dictStore.getDictDefault('menu-type').dictValue as number
  drawerDetailFormModel.menuStatus = dictStore.getDictDefault('common-status').dictValue as number
}
const handleClickAddChildren = (row: SysMenu) => {
  initDrawerDetail(ModeTypeEnum.ADD);
  drawerDetailFormModel.menuType = dictStore.getDictDefault('menu-type').dictValue as number
  drawerDetailFormModel.menuStatus = dictStore.getDictDefault('common-status').dictValue as number
  drawerDetailFormModel.parentId = row.id
}
const handleClickDetail = (row: SysMenu) => {
  if (row.id != null) {
    menuApi.detail(row.id).then(res => {
      Object.assign(drawerDetailFormModel, res.data)
      initDrawerDetail(ModeTypeEnum.DETAIL)
    })
  }
}
const handleClickModify = (row: SysMenu) => {
  if (row.id != null) {
    menuApi.detail(row.id).then(res => {
      Object.assign(drawerDetailFormModel, res.data)
      initDrawerDetail(ModeTypeEnum.MODIFY)
    })
  }
}
const handleClickDelete = (row: SysMenu) => {
  if (row.id != null) {
    menuApi.delete(row.id).then(res => {
      window.$message?.success(res.message)
      handleClickQuery()
    })
  }
}
const handleDrawerClose = () => {
  drawerDetailFormRef.value?.restoreValidation()
  resetToNull(drawerDetailFormModel)
  handleClickQuery()
}

const handleClickSubmit = () => {
  drawerDetailFormRef.value?.validate((errors) => {
    if (!errors) {
      drawerDetailFormLoading.value = true
      switch (drawerDetailTitle.value) {
        case ModeTypeEnum.ADD:
          menuApi.add(drawerDetailFormModel).then((res) => {
            window.$message?.success(res.message)
            drawerDetailShow.value = false
          })
          break;
        case ModeTypeEnum.MODIFY:
          menuApi.modify(drawerDetailFormModel).then((res) => {
            window.$message?.success(res.message)
            drawerDetailShow.value = false
          })
          break;
      }
    }
  }).finally(() => drawerDetailFormLoading.value = false)
}
const handleClickWeights = (row: SysMenu) => {
  menuApi.list({parentId: row.id}).then(res => {
    modalWeightsModel.value = res.data
    modalWeightsShow.value = true
    modalParentId.value = row.id
  })
}
const handleChangeWeightsModel = (data: SysMenu[]) => {
  modalWeightsModel.value = data
}
const handleClickSubmitWeights = () => {
  if (modalParentId.value) {
    modalWeightsSubmitLoading.value = true
    menuApi.updateWeights(
        modalParentId.value,
        modalWeightsModel.value.map((e, index) => {
          return {id: e.id, menuWeight: index}
        })
    ).then(res => {
      window.$message?.success(res.message)
      modalWeightsShow.value = false
      handleClickQuery()
    }).finally(() => modalWeightsSubmitLoading.value = false)
  }
}
const handleNoSideSpace = (value: string) => {
  return !value.startsWith(' ') && !value.endsWith(' ')
}
</script>

<template>
  <n-card class="menu-manage">
    <template #header>
      <n-space>
        <n-button
            secondary
            @click="handleClickQuery"
            :loading="tableLoading"
            v-p="'menu:tree'"
        >查询
        </n-button>
        <n-button
            secondary
            type="primary"
            @click="handleAddMenu"
            v-p="'menu:add'"
        >新增
        </n-button>
      </n-space>
    </template>
    <n-data-table
        remote
        :loading="tableLoading"
        :columns="tableColumns"
        :data="tableData"
        :row-key="(row:SysMenu) => row.id"
    />
  </n-card>
  <n-drawer
      v-model:show="drawerDetailShow"
      :width="502"
      :on-after-leave="handleDrawerClose"
  >
    <n-drawer-content
        :title="drawerDetailTitle"
        closable
    >
      <n-form
          ref="drawerDetailFormRef"
          :model="drawerDetailFormModel"
          :rules="drawerDetailFormRules"
          :disabled="drawerDetailTitle === ModeTypeEnum.DETAIL"
      >
        <n-grid :cols="24" :x-gap="12">
          <n-form-item-gi v-if="drawerDetailFormModel.id" :span="24" label="主键">
            <n-tag type="primary">{{ drawerDetailFormModel.id }}</n-tag>
          </n-form-item-gi>
          <n-form-item-gi :span="24" label="上级菜单">
            <n-tree-select
                clearable
                :options="drawerDetailFormParentTreeOptions"
                :render-label="drawerDetailFormTreeLabel"
                label-field="menuName"
                key-field="id"
                v-model:value="drawerDetailFormModel.parentId"
            />
          </n-form-item-gi>
          <n-form-item-gi :span="12" label="菜单名称" path="menuName">
            <n-input v-model:value="drawerDetailFormModel.menuName" placeholder="请输入菜单名称"/>
          </n-form-item-gi>
          <n-form-item-gi :span="12" label="菜单编码" path="permissionCode">
            <n-input v-model:value="drawerDetailFormModel.permissionCode" :allow-input="handleNoSideSpace"
                     placeholder="请输入菜单编码"/>
          </n-form-item-gi>
          <n-form-item-gi :span="12" label="菜单状态" path="menuStatus">
            <n-switch v-model:value="drawerDetailFormModel.menuStatus" :checked-value="1" :unchecked-value="0"/>
          </n-form-item-gi>
          <n-form-item-gi :span="12" label="菜单排序" path="menuWeight">
            <n-input-number v-model:value="drawerDetailFormModel.menuWeight" placeholder="请输入菜单排序"
                            :default-value="0"/>
          </n-form-item-gi>
          <n-form-item-gi :span="24" label="菜单类型">
            <n-radio-group v-model:value="drawerDetailFormModel.menuType" name="menuType"
                           :disabled="drawerDetailTitle !== ModeTypeEnum.ADD">
              <n-radio-button
                  v-for="type in dictStore.getDictList('menu-type')"
                  :key="type.dictValue"
                  :value="type.dictValue"
                  :label="type.dictName"
              />
            </n-radio-group>
          </n-form-item-gi>
          <n-form-item-gi :span="6" path="httpMethod"
                          v-if="drawerDetailFormModel.menuType === MenuTypeEnum.ENDPOINT">
            <template #label>
              <n-tooltip trigger="hover">
                <template #trigger>
                  请求方式
                </template>
                请求方式属性不会参与权限校验,主要用于接口属性的补全
              </n-tooltip>
            </template>
            <n-select
                v-model:value="drawerDetailFormModel.httpMethod"
                :options="dictStore.getDictTagOptions('http-method')"
                placeholder="请求方式"
                clearable
            />
          </n-form-item-gi>
          <n-form-item-gi :span="drawerDetailFormModel.menuType === MenuTypeEnum.ENDPOINT ? 18: 24" label="菜单路径"
                          v-if="drawerDetailFormModel.menuType !== MenuTypeEnum.BUTTON"
                          path="menuPath">
            <n-input v-model:value="drawerDetailFormModel.menuPath" placeholder="请输入菜单路径"/>
          </n-form-item-gi>
          <template
              v-if="drawerDetailFormModel.menuType === MenuTypeEnum.DIRECTORY || drawerDetailFormModel.menuType === MenuTypeEnum.MENU">
            <n-form-item-gi :span="24" label="组件路径" v-if="drawerDetailFormModel.menuType === MenuTypeEnum.MENU"
                            path="componentPath">
              <n-input v-model:value="drawerDetailFormModel.componentPath" placeholder="请输入组件路径"/>
            </n-form-item-gi>
            <n-form-item-gi :span="24" label="菜单图标" path="menuIcon">
              <icon-picker v-model:value="drawerDetailFormModel.menuIcon"
                           :disabled="drawerDetailTitle === ModeTypeEnum.DETAIL"/>
            </n-form-item-gi>
          </template>
        </n-grid>
      </n-form>
      <template v-if="drawerDetailTitle !== ModeTypeEnum.DETAIL" #footer>
        <n-button type="primary" @click="handleClickSubmit">提交</n-button>
      </template>
    </n-drawer-content>
  </n-drawer>
  <n-modal
      v-model:show="modalWeightsShow"
      preset="card"
      title="菜单排序"
      size="huge"
      :style="{width: '600px'}"
  >
    <n-scrollbar max-h-450px>
      <reorderable-list :data="modalWeightsModel" @change="handleChangeWeightsModel">
        <template #item="{item}">
          <n-space align="center">
            <n-tag type="info" round>{{ item.menuWeight }}</n-tag>
            <n-text>{{ item.menuName }}</n-text>
            <dict-tag dict-code="menu-type" :dict-value="item.menuType"/>
          </n-space>
        </template>
      </reorderable-list>
    </n-scrollbar>
    <template #footer>
      <n-button
          float-right
          type="primary"
          @click="handleClickSubmitWeights"
          :loading="modalWeightsSubmitLoading"
      >提交
      </n-button>
    </template>
  </n-modal>
</template>

<style scoped>

</style>
